#include "evt_loop.h"

namespace qio {

class EventLoopCallbacks {
 public:
  static void LoopAsyncTaskCallback(uv_async_t *async) QIO_NOEXCEPT {  // NOLINT
    auto *evtLoop = static_cast<EventLoop *>(async->data);

    evtLoop->lock_.lock();
    std::list<EventLoop::RunCallback> task_queue;
    task_queue.swap(evtLoop->request_queue_);
    evtLoop->lock_.unlock();

    // pop task
    while (!task_queue.empty()) {
      const auto &control_req = task_queue.front();
      control_req();
      task_queue.pop_front();
    }
  }

  static void LoopAsyncStopCallback(uv_handle_t *hd) QIO_NOEXCEPT {
    // async stop call
    auto *evtLoop = static_cast<EventLoop *>(hd->data);
    // call loop will stop
    evtLoop->ShouldCleanupLoop();
  }
};

EventLoop::~EventLoop() QIO_NOEXCEPT {
  delete async_;
  delete loop_;
}

EventLoop::EventLoop() QIO_NOEXCEPT : lock_(),
                                      request_queue_(),
                                      loop_(new uv_loop_t),
                                      async_(new uv_async_t),
                                      loop_state_(INIT),
                                      loop_thread_id_(),
                                      loop_stop_cb_() {
  assert(loop_state_.is_lock_free());
  uv_loop_init(loop_);
  uv_async_init(loop_, async_, &EventLoopCallbacks::LoopAsyncTaskCallback);
  async_->data = this;
}

void EventLoop::Run() QIO_NOEXCEPT {
  assert(loop_state_.load(std::memory_order_relaxed) == INIT);
  // running
  RunInLoop(
      [this]() { loop_state_.store(RUNNING, std::memory_order_relaxed); });
  loop_thread_id_ = std::this_thread::get_id();
  uv_run(loop_, UV_RUN_DEFAULT);
  uv_loop_close(loop_);
  loop_state_.store(STOPPED, std::memory_order_release);
}

void EventLoop::RunInLoop(RunCallback &&reqFun) QIO_NOEXCEPT {
  assert(reqFun);
  if (!IsInLoop()) {
    {
      // enqueue
      std::unique_lock<std::recursive_mutex> guard(lock_);
      request_queue_.emplace_back(std::forward<RunCallback>(reqFun));
    }
    if (loop_state_.load(std::memory_order_relaxed) <= POST_STOP) {
      uv_async_send(async_);
    }
  } else {
    // direct invoke
    reqFun();
  }
}

SharedTimer EventLoop::RunEvery(std::size_t ms,
                                Timer::TimerCallback &&cb) QIO_NOEXCEPT {
  assert(ms > 0 && cb != nullptr);
  auto shared_timer = std::make_shared<Timer>(this);
  shared_timer->OnTimeout([shared_timer, ms, cb](Timer *timer) {
    cb(timer);
    if (timer->GetStat() != Timer::kRunning) {
      return;
    }
    timer->Start(ms);
  });
  shared_timer->OnStop([this, shared_timer](Timer *timer) {
    // timer stopped
    timer_set_.erase(shared_timer);
  });
  timer_set_.insert(shared_timer);
  RunInLoop([ms, shared_timer]() { shared_timer->Start(ms); });
  return shared_timer;
}  // namespace qio

SharedTimer EventLoop::RunAfter(std::size_t ms,
                                Timer::TimerCallback &&cb) QIO_NOEXCEPT {
  assert(ms > 0 && cb != nullptr);
  auto shared_timer = std::make_shared<Timer>(this);
  shared_timer->OnTimeout([shared_timer, cb](Timer *timer) {
    cb(timer);
    if (timer->GetStat() >= Timer::kStopping) {
      return;
    }
    timer->Stop();
  });
  shared_timer->OnStop([this, shared_timer](Timer *timer) {
    // timer stopped
    timer_set_.erase(shared_timer);
  });
  timer_set_.insert(shared_timer);
  RunInLoop([ms, shared_timer]() { shared_timer->Start(ms); });
  return shared_timer;
}

// send close control cmd
void EventLoop::Stop() QIO_NOEXCEPT {
  // 如果不再运行,则立即退出
  static LoopState expect_init = INIT;
  if (loop_state_.compare_exchange_strong(expect_init, STOPPED,
                                          std::memory_order_relaxed)) {
    return;
  }
  //如果正在允许运行,则设置为POST_STOP
  static LoopState expect_running = RUNNING;
  if (!loop_state_.compare_exchange_strong(expect_running, POST_STOP,
                                           std::memory_order_relaxed)) {
    return;
  }

  auto close_fn = [this]() {
    auto *async_handle = reinterpret_cast<uv_handle_t *>(this->async_);
    loop_state_.store(STOPPING, std::memory_order_relaxed);
    uv_close(async_handle, &EventLoopCallbacks::LoopAsyncStopCallback);
  };

  if (IsInLoop()) {
    close_fn();
  } else {
    RunInLoop(close_fn);
  }
}

void EventLoop::ShouldCleanupLoop() QIO_NOEXCEPT {
  for (auto &v : timer_set_) {
    v->Stop();
  }
  std::list<RunCallback> empty;
  request_queue_.swap(empty);
  if (loop_stop_cb_) {
    loop_stop_cb_();
  }
  loop_stop_cb_ = nullptr;
}

}  // namespace qio
